use std::fmt::Debug;

// 没有返回值，不用标注生命周期
fn print(s: &str) {
    println!("s: {}", s)
}

// 不是引用时，不用标注生命周期
fn clone(s: &str) -> String {
    s.to_owned()
}

// 在返回引用时，只有一个参数和返回值，Rust内部自动帮忙标注隐式生命周期
fn get(s: &str) -> &str {
    s
}

// 不确定返回的引用，使用where来限定，对生命周期进行约束，'a: 'out表示'a的生命周期等于或大于'out，同理'b: 'out表示'b的生命周期等于或大于'out
fn long<'a, 'b, 'out>(s1: &'a str, s2: &'b str) -> &'out str
where
    'a: 'out,
    'b: 'out,
{
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

// 'static静态生命周期，标注值生命周期直到程序结束，比较耗费性能，一般在处理生命周期比较烦的时候又编译不过时临时使用一下
fn urgent(s1: &'static str, s2: &'static str) -> &'static str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}
// 对比组，与下面Person结构体
struct Point {
    x: i32,
    y: i32,
}

// 在结构体中，使用了引用类型，需要标注生命周期，并且在impl实现中同步需要标注生命周期
#[derive(Debug)]
struct Person<'a> {
    name: &'a str,
}

impl<'a> Person<'a> {
    fn get_name(&self) -> &str {
        // 自动解引用强制特性，self.name = (*self).name
        self.name
    }
}

fn main() {
    // 函数
    print("hello rust");

    let s = clone("hello rust");
    println!("s: {}", s);

    let s = get("hello rust");
    println!("s: {}", s);

    let s = long("hello world", "hello rust");
    println!("s: {}", s);

    let s = urgent("hello world", "hello rust");
    println!("s: {}", s);

    // 结构体
    let name = "danile"; // 引用具有copy特质
    let p = Person { name: name };
    println!("p: {:?}, name: {}", p, p.get_name());

    /*
     * 生命周期约束
     *   1. 'a: 'b 表示'a的生命周期不少于'b
     *   2. T : 'a 表示T类型的生命周期不少'a
     */

    // 对&'static T和T:'static的理解
    //   1. &'static T 表示T类型生命周期必须是'static，即直到程序结束。
    //   2. T:'static表示T类型的生命周期大于'statich或能安全无限期持有，
    //      即直到程序结束。但这里的T类型可以是引用类型，也可以是非引用
    //      类型。当是引用类型时，等同于 &'static T，当是非引用类型时，
    //      类型必须是所有权类型

    // 下面print_it_1和print_it_2都是对T类型的生命周期进行约束，区别在
    // print_it_2传入的是引用，但重点还是在于T类型的生命周期
    fn print_it_1<T: Debug + 'static>(input: T) {
        println!("符合 T:'static is: {:?}", input);
    }

    fn print_it_2<'a, T: Debug + 'static>(input: &'a T) {
        println!("符合 T:'static is: {:?}", input);
    }

    // 这里s1的值所有权类型，并且s1变量对值有绝对的控制权，可以确保给print_it_1和print_it_2值是所有权类型
    let s1 = "rust".to_string();
    print_it_2(&s1); // ok
    print_it_1(s1); // ok

    // 这里s1的值所有权类型，并且s1变量对值有绝对的控制权，虽然赋值给了s2，但是交出了所有权，s2变量拥有了绝对的控制权，仍可以确保给print_it_1和print_it_2值是所有权类型
    let s1 = "rust".to_string();
    let s2 = s1;
    print_it_2(&s2); // ok
    print_it_1(s2); // ok

    // 这里s1的值是所有权类型，s1赋值s2是交出了&String（在main函数中创建引用，被存放在栈上），无法确保s2的引用永久存在，所以编译不通过
    let s1 = "rust".to_string();
    let s2 = &s1; // 引用类型，在main函数中创建引用，被存放在栈上，无法确保s2的引用永久存在
    print_it_2(&s2); // bad
    print_it_1(s2); // bad

    // i32是非所有权类型，但s1拥有了所有权，所以编译通过
    let s1 = 1;
    print_it_2(&s1); // ok
    print_it_1(s1); // ok

    // i32是非所有权类型，但s2持有的引用不能被保证永久安全，所以编译不能通过
    let s1 = 1;
    let s2 = &s1;
    print_it_2(&s2); // ok
    print_it_1(s2); // ok


    // Vector是所有权类型，所以编译通过
    let s1 = vec![1, 2, 3];
    print_it_2(&s1); // ok
    print_it_1(s1); // ok

    // Vector是所有权类型，但s2持有的引用不能被保证永久安全，所以编译不能通过
    let s1 = vec![1, 2, 3];
    let s2= &s1;
    print_it_2(&s2); // bad
    print_it_1(s2); // bad

    // struct不是所有权类型，但s1拥有了所有权，所以编译通过
    #[derive(Debug)]
    struct A<T>(T);
    let s1 = A(0);
    print_it_2(&s1); // ok
    print_it_1(s1); // ok

    // strucs不是所有权类型，但s1拥有了所有权，所以编译通过
    #[derive(Debug)]
    struct B<'a, T>(&'a T);
    let s1 = B(&0);
    print_it_2(&s1); // ok
    print_it_1(s1); // ok

    // 下面Ok结构体，传入的值是引用类型，生命周期标注为'a，表示Ok结构体的生命周期与传入的引用类型一致，当s1是字符串字面量时，生命周期是'static，所以Ok结构体的生命周期也是'static，下面print_it_1和print_it_2传入Ok结构体满足约束
    #[derive(Debug)]
    struct Ok<'a>(&'a str);
    let s1 = "rust";
    let s2 = Ok(&s1);
    print_it_2(&s2); // ok
    print_it_1(s2); // ok

    //
    #[derive(Debug)]
    struct Bad<'a>(&'a String);
    let s1 = "rust".to_string();
    let s2 = Bad(&s1); 
    print_it_2(&s2); // bad
    print_it_1(s2); // bad
}
